/*! @azure/msal-browser v2.38.3 2023-10-27 */
'use strict';
import { __awaiter, __generator } from '../../_virtual/_tslib.js';
import { NativeConstants, NativeExtensionMethod } from '../../utils/BrowserConstants.js';
import { AuthError, AuthenticationScheme, PerformanceEvents } from '@azure/msal-common';
import { NativeAuthError } from '../../error/NativeAuthError.js';
import { BrowserAuthError } from '../../error/BrowserAuthError.js';

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
var NativeMessageHandler = /** @class */ (function () {
    function NativeMessageHandler(logger, handshakeTimeoutMs, performanceClient, extensionId) {
        this.logger = logger;
        this.handshakeTimeoutMs = handshakeTimeoutMs;
        this.extensionId = extensionId;
        this.resolvers = new Map(); // Used for non-handshake messages
        this.handshakeResolvers = new Map(); // Used for handshake messages
        this.responseId = 0;
        this.messageChannel = new MessageChannel();
        this.windowListener = this.onWindowMessage.bind(this); // Window event callback doesn't have access to 'this' unless it's bound
        this.performanceClient = performanceClient;
        this.handshakeEvent = performanceClient.startMeasurement(PerformanceEvents.NativeMessageHandlerHandshake);
    }
    /**
     * Sends a given message to the extension and resolves with the extension response
     * @param body
     */
    NativeMessageHandler.prototype.sendMessage = function (body) {
        return __awaiter(this, void 0, void 0, function () {
            var req;
            var _this = this;
            return __generator(this, function (_a) {
                this.logger.trace("NativeMessageHandler - sendMessage called.");
                req = {
                    channel: NativeConstants.CHANNEL_ID,
                    extensionId: this.extensionId,
                    responseId: this.responseId++,
                    body: body
                };
                this.logger.trace("NativeMessageHandler - Sending request to browser extension");
                this.logger.tracePii("NativeMessageHandler - Sending request to browser extension: " + JSON.stringify(req));
                this.messageChannel.port1.postMessage(req);
                return [2 /*return*/, new Promise(function (resolve, reject) {
                        _this.resolvers.set(req.responseId, { resolve: resolve, reject: reject });
                    })];
            });
        });
    };
    /**
     * Returns an instance of the MessageHandler that has successfully established a connection with an extension
     * @param {Logger} logger
     * @param {number} handshakeTimeoutMs
     * @param {IPerformanceClient} performanceClient
     */
    NativeMessageHandler.createProvider = function (logger, handshakeTimeoutMs, performanceClient) {
        return __awaiter(this, void 0, void 0, function () {
            var preferredProvider, backupProvider;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        logger.trace("NativeMessageHandler - createProvider called.");
                        _a.label = 1;
                    case 1:
                        _a.trys.push([1, 3, , 5]);
                        preferredProvider = new NativeMessageHandler(logger, handshakeTimeoutMs, performanceClient, NativeConstants.PREFERRED_EXTENSION_ID);
                        return [4 /*yield*/, preferredProvider.sendHandshakeRequest()];
                    case 2:
                        _a.sent();
                        return [2 /*return*/, preferredProvider];
                    case 3:
                        _a.sent();
                        backupProvider = new NativeMessageHandler(logger, handshakeTimeoutMs, performanceClient);
                        return [4 /*yield*/, backupProvider.sendHandshakeRequest()];
                    case 4:
                        _a.sent();
                        return [2 /*return*/, backupProvider];
                    case 5: return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Send handshake request helper.
     */
    NativeMessageHandler.prototype.sendHandshakeRequest = function () {
        return __awaiter(this, void 0, void 0, function () {
            var req;
            var _this = this;
            return __generator(this, function (_a) {
                this.logger.trace("NativeMessageHandler - sendHandshakeRequest called.");
                // Register this event listener before sending handshake
                window.addEventListener("message", this.windowListener, false); // false is important, because content script message processing should work first
                req = {
                    channel: NativeConstants.CHANNEL_ID,
                    extensionId: this.extensionId,
                    responseId: this.responseId++,
                    body: {
                        method: NativeExtensionMethod.HandshakeRequest
                    }
                };
                this.handshakeEvent.addStaticFields({
                    extensionId: this.extensionId,
                    extensionHandshakeTimeoutMs: this.handshakeTimeoutMs
                });
                this.messageChannel.port1.onmessage = function (event) {
                    _this.onChannelMessage(event);
                };
                window.postMessage(req, window.origin, [this.messageChannel.port2]);
                return [2 /*return*/, new Promise(function (resolve, reject) {
                        _this.handshakeResolvers.set(req.responseId, { resolve: resolve, reject: reject });
                        _this.timeoutId = window.setTimeout(function () {
                            /*
                             * Throw an error if neither HandshakeResponse nor original Handshake request are received in a reasonable timeframe.
                             * This typically suggests an event handler stopped propagation of the Handshake request but did not respond to it on the MessageChannel port
                             */
                            window.removeEventListener("message", _this.windowListener, false);
                            _this.messageChannel.port1.close();
                            _this.messageChannel.port2.close();
                            _this.handshakeEvent.endMeasurement({ extensionHandshakeTimedOut: true, success: false });
                            reject(BrowserAuthError.createNativeHandshakeTimeoutError());
                            _this.handshakeResolvers.delete(req.responseId);
                        }, _this.handshakeTimeoutMs); // Use a reasonable timeout in milliseconds here
                    })];
            });
        });
    };
    /**
     * Invoked when a message is posted to the window. If a handshake request is received it means the extension is not installed.
     * @param event
     */
    NativeMessageHandler.prototype.onWindowMessage = function (event) {
        this.logger.trace("NativeMessageHandler - onWindowMessage called");
        // We only accept messages from ourselves
        if (event.source !== window) {
            return;
        }
        var request = event.data;
        if (!request.channel || request.channel !== NativeConstants.CHANNEL_ID) {
            return;
        }
        if (request.extensionId && request.extensionId !== this.extensionId) {
            return;
        }
        if (request.body.method === NativeExtensionMethod.HandshakeRequest) {
            // If we receive this message back it means no extension intercepted the request, meaning no extension supporting handshake protocol is installed
            this.logger.verbose(request.extensionId ? "Extension with id: " + request.extensionId + " not installed" : "No extension installed");
            clearTimeout(this.timeoutId);
            this.messageChannel.port1.close();
            this.messageChannel.port2.close();
            window.removeEventListener("message", this.windowListener, false);
            var handshakeResolver = this.handshakeResolvers.get(request.responseId);
            if (handshakeResolver) {
                this.handshakeEvent.endMeasurement({ success: false, extensionInstalled: false });
                handshakeResolver.reject(BrowserAuthError.createNativeExtensionNotInstalledError());
            }
        }
    };
    /**
     * Invoked when a message is received from the extension on the MessageChannel port
     * @param event
     */
    NativeMessageHandler.prototype.onChannelMessage = function (event) {
        this.logger.trace("NativeMessageHandler - onChannelMessage called.");
        var request = event.data;
        var resolver = this.resolvers.get(request.responseId);
        var handshakeResolver = this.handshakeResolvers.get(request.responseId);
        try {
            var method = request.body.method;
            if (method === NativeExtensionMethod.Response) {
                if (!resolver) {
                    return;
                }
                var response = request.body.response;
                this.logger.trace("NativeMessageHandler - Received response from browser extension");
                this.logger.tracePii("NativeMessageHandler - Received response from browser extension: " + JSON.stringify(response));
                if (response.status !== "Success") {
                    resolver.reject(NativeAuthError.createError(response.code, response.description, response.ext));
                }
                else if (response.result) {
                    if (response.result["code"] && response.result["description"]) {
                        resolver.reject(NativeAuthError.createError(response.result["code"], response.result["description"], response.result["ext"]));
                    }
                    else {
                        resolver.resolve(response.result);
                    }
                }
                else {
                    throw AuthError.createUnexpectedError("Event does not contain result.");
                }
                this.resolvers.delete(request.responseId);
            }
            else if (method === NativeExtensionMethod.HandshakeResponse) {
                if (!handshakeResolver) {
                    return;
                }
                clearTimeout(this.timeoutId); // Clear setTimeout
                window.removeEventListener("message", this.windowListener, false); // Remove 'No extension' listener
                this.extensionId = request.extensionId;
                this.extensionVersion = request.body.version;
                this.logger.verbose("NativeMessageHandler - Received HandshakeResponse from extension: " + this.extensionId);
                this.handshakeEvent.endMeasurement({ extensionInstalled: true, success: true });
                handshakeResolver.resolve();
                this.handshakeResolvers.delete(request.responseId);
            }
            // Do nothing if method is not Response or HandshakeResponse
        }
        catch (err) {
            this.logger.error("Error parsing response from WAM Extension");
            this.logger.errorPii("Error parsing response from WAM Extension: " + err.toString());
            this.logger.errorPii("Unable to parse " + event);
            if (resolver) {
                resolver.reject(err);
            }
            else if (handshakeResolver) {
                handshakeResolver.reject(err);
            }
        }
    };
    /**
     * Returns the Id for the browser extension this handler is communicating with
     * @returns
     */
    NativeMessageHandler.prototype.getExtensionId = function () {
        return this.extensionId;
    };
    /**
     * Returns the version for the browser extension this handler is communicating with
     * @returns
     */
    NativeMessageHandler.prototype.getExtensionVersion = function () {
        return this.extensionVersion;
    };
    /**
     * Returns boolean indicating whether or not the request should attempt to use native broker
     * @param logger
     * @param config
     * @param nativeExtensionProvider
     * @param authenticationScheme
     */
    NativeMessageHandler.isNativeAvailable = function (config, logger, nativeExtensionProvider, authenticationScheme) {
        logger.trace("isNativeAvailable called");
        if (!config.system.allowNativeBroker) {
            logger.trace("isNativeAvailable: allowNativeBroker is not enabled, returning false");
            // Developer disabled WAM
            return false;
        }
        if (!nativeExtensionProvider) {
            logger.trace("isNativeAvailable: WAM extension provider is not initialized, returning false");
            // Extension is not available
            return false;
        }
        if (authenticationScheme) {
            switch (authenticationScheme) {
                case AuthenticationScheme.BEARER:
                case AuthenticationScheme.POP:
                    logger.trace("isNativeAvailable: authenticationScheme is supported, returning true");
                    return true;
                default:
                    logger.trace("isNativeAvailable: authenticationScheme is not supported, returning false");
                    return false;
            }
        }
        return true;
    };
    return NativeMessageHandler;
}());

export { NativeMessageHandler };
//# sourceMappingURL=NativeMessageHandler.js.map
